package cn.shanke.imousdk.core;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import org.apache.http.client.fluent.Request;
import org.apache.http.entity.StringEntity;

import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

public class SdkClient {
    public static String APP_ID;
    public static String APP_SECRET;

    public static final String BASE_API = "https://openapi.lechange.cn/openapi";
    public static final String GET_ACCESS_TOKEN = "/accessToken";
    public static final String GET_KIT_TOKEN = "/getKitToken";

    public static final String CODE_SUCCESS = "0";

    public static String ACCESS_TOKEN = null;
    public static Long   ACCESS_TOKEN_EXPIRE = null;

    public static Map<String, Token> KIT_TOKEN = new ConcurrentHashMap<>(); // 轻应用token

    public static void main(String[] args){
        SdkClient.setup("appId", "appSecret");
        int i = 0;
        while (i<3) {
            System.out.println(getKitToken("5K0516FPAZ729F0", "1"));
            try {
                TimeUnit.SECONDS.sleep(5);
//                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            i++;
        }
    }

    public static void setup(String appId, String appSecret) {
        APP_ID = appId;
        APP_SECRET = appSecret;
    }

    public static String getSign(long time, String nonce) {
        String oriSign = String.format("time:%s,nonce:%s,appSecret:%s", time, nonce, APP_SECRET);
        return MD5(oriSign);
    }

    public static String MD5(String str) {
        char hexDigits[] = { // 用来将字节转换成 16 进制表示的字符
                '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
                'e', 'f' };
        try {
            byte[] strTemp = str.getBytes();
            MessageDigest mdTemp = MessageDigest.getInstance("MD5");
            mdTemp.update(strTemp);
            byte tmp[] = mdTemp.digest(); // MD5 的计算结果是一个 128 位的长整数，
            // 用字节表示就是 16 个字节
            char strs[] = new char[16 * 2]; // 每个字节用 16 进制表示的话，使用两个字符，
            // 所以表示成 16 进制需要 32 个字符
            int k = 0; // 表示转换结果中对应的字符位置
            for (int i = 0; i < 16; i++) { // 从第一个字节开始，对 MD5 的每一个字节
                // 转换成 16 进制字符的转换
                byte byte0 = tmp[i]; // 取第 i 个字节
                strs[k++] = hexDigits[byte0 >>> 4 & 0xf]; // 取字节中高 4 位的数字转换,
                // >>> 为逻辑右移，将符号位一起右移
                strs[k++] = hexDigits[byte0 & 0xf]; // 取字节中低 4 位的数字转换
            }
//            return new String(strs).toUpperCase(); // 换后的结果转换为字符串
            return new String(strs); // 换后的结果转换为字符串
        } catch (Exception e) {
            return null;
        }
    }

    public static JSONObject getBaseRequestDate() {
        /**
         * {
         *     "system":{
         *         "ver":"1.0",
         *         "appId":"lcd08b4b07ce224508",
         *         "sign":"b7e5bbcc6cc07941725d9ad318883d8e",
         *         "time":1599013514,
         *         "nonce":"fbf19fc6-17a1-4f73-a967-75eadbc805a2"
         *     },
         *     "id":"98a7a257-c4e4-4db3-a2d3-d97a3836b87c",
         *     "params":{
         *
         *     }
         * }
         */
        long time = System.currentTimeMillis() / 1000;
        String nonce = UUID.randomUUID().toString();
        JSONObject data = new JSONObject();
        data.put("system", new JSONObject()
                .fluentPut("ver", "1.0")
                .fluentPut("appId", APP_ID)
                .fluentPut("sign", getSign(time, nonce))
                .fluentPut("time", time)
                .fluentPut("nonce", nonce)
        );
        data.put("id", UUID.randomUUID().toString());

        return data;
    }

    public synchronized static String getAccessToken() {
        if (APP_ID == null || APP_SECRET == null
            || APP_ID.length() == 0 ||  APP_SECRET.length() == 0
        ) {
            throw new RuntimeException("[IMOU-SDK]请调用SdkClient.setup进行初始化");
        }
        if (ACCESS_TOKEN == null || (ACCESS_TOKEN_EXPIRE - System.currentTimeMillis())/1000/60/60 > 70) { // 有效期72小时，这里70小时就获取新的
            try {
                JSONObject data = getBaseRequestDate();
                String response = Request.Post(BASE_API + GET_ACCESS_TOKEN).addHeader("Content-Type", "application/json")
                        .body(new StringEntity(data.toJSONString(), Charset.forName("UTF-8")))
                        .execute()
                        .returnContent().asString();
                if (response != null && response.length() > 0) {
                    /**
                     * {
                     *     "result":{
                     *         "msg":"操作成功。",
                     *         "code":"0",
                     *         "data":{
                     *             "expireTime":259176,
                     *             "accessToken":"At_00006ac6e32d123141238f60147de7ec"
                     *         }
                     *     },
                     *     "id":"98a7a257-c4e4-4db3-a2d3-d97a3836b87c"
                     * }
                     */
                    JSONObject jo = JSONObject.parseObject(response);
                    JSONObject result = jo != null ? jo.getJSONObject("result") : null;
                    if (result != null) {
                        String code = result.getString("code");
                        if (CODE_SUCCESS.equals(code)) {
                            int expireTime = Integer.parseInt(JSONPath.read(response, "$.result.data.expireTime")+"");
                            ACCESS_TOKEN = (String)JSONPath.read(response, "$.result.data.accessToken");
                            ACCESS_TOKEN_EXPIRE = System.currentTimeMillis() + expireTime*1000;
                        } else {
                            System.err.printf("[IMOU-SDK]获取accessToken失败：%s | %s", code, result.getString("msg"));
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return ACCESS_TOKEN;
    }

    public static String kitTokenKey(String deviceId, String channelId) {
        return new StringBuffer(deviceId).append("#").append(channelId).toString();
    }
    public static String getValidKitToken(String deviceId, String channelId) {
        Token token = KIT_TOKEN.get(kitTokenKey(deviceId, channelId));
        if (token != null) {
            if (token.getValue() == null || (token.getExpire() - System.currentTimeMillis())/1000/60 > 100) { // 有效期120分钟，这里100分钟就获取新的
                return token.getValue();
            }
        }
        return null;
    }
    public synchronized static String getKitToken(String deviceId, String channelId) {
        String kitToken = getValidKitToken(deviceId, channelId);
        if (kitToken == null) {
            JSONObject data = getBaseRequestDate();
            /**
             * {
             *     "params":{
             *         "token":"授权token",
             *         "deviceId":"设备ID",
             *         "channelId":"通道号",
             *         "type":"0"
             *     }
             * }
             */
            data.put("params", new JSONObject()
                    .fluentPut("token", getAccessToken())
                    .fluentPut("deviceId", deviceId)
                    .fluentPut("channelId", channelId)
                    .fluentPut("type", "0") //0：所有权限；1：实时预览；2：录像回放（云录像+本地录像）
            );
            try {
                String response = Request.Post(BASE_API + GET_KIT_TOKEN).addHeader("Content-Type", "application/json")
                        .body(new StringEntity(data.toJSONString(), Charset.forName("UTF-8")))
                        .execute()
                        .returnContent().asString();
                if (response != null && response.length() > 0) {
                    /**
                     * {
                     *     "id":"123456",
                     *     "result":{
                     *         "code":"0",
                     *         "msg":"操作成功",
                     *         "data":{
                     *             "expireTime": 84323,//过期剩余秒数
                     *             "kitToken":"Kt_e6cf503603b848098376bc2dc1c6f38d" //轻应用授权token，新获取的kitToken有效期为2个小时;
                     *       }
                     *     }
                     * }
                     */
                    JSONObject jo = JSONObject.parseObject(response);
                    JSONObject result = jo != null ? jo.getJSONObject("result") : null;
                    if (result != null) {
                        String code = result.getString("code");
                        if (CODE_SUCCESS.equals(code)) {
                            int expireTime = Integer.parseInt(JSONPath.read(response, "$.result.data.expireTime")+"");
                            kitToken = (String)JSONPath.read(response, "$.result.data.kitToken");
                            KIT_TOKEN.put(kitTokenKey(deviceId, channelId),
                                    new Token(kitToken,System.currentTimeMillis() + expireTime*1000)
                            );
                        } else {
                            System.err.printf("[IMOU-SDK]获取kitToken失败：%s | %s", code, result.getString("msg"));
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return kitToken;
    }
}
